package com.csaba.swing.gui.wizard;

import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.AbstractButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.JTextComponent;

import com.csaba.swing.gui.util.DynamicColumnLayout;

/**
 * @author Gabe
 * 
 * This class has to be subclassed to create a new page for a WizardDialog. 
 * The WizardDialog invokes different methods to prepare view content, and process
 * user input in the appropriate time.
 *
 */
@SuppressWarnings( "serial" )
public abstract class WizardPage extends JPanel
{
	public final static int ACTION_FINISH = 0;
	public final static int ACTION_CANCEL = 1;
	public final static int ACTION_BACK = 2;
	public final static int ACTION_NEXT = 3;

	protected WizardContext context;

	public WizardPage()
	{
		super(DynamicColumnLayout.getDefault());
	}

	/**
	 * It is invoked before the page first appears on the GUI. The page should
	 * build page components at this stage.
	 * 
	 * @param context Init receives the WizardContext that can be used for sharing information
	 * between WizardPages.
	 */
	public void init( final WizardContext context )
	{
		this.context = context;
	}

	/**
	 * This method is invoked after the page appeared, but before the user
	 * can use components. Here you can update field contents, if requires 
	 * some longer procedure.
	 */
	public void updateView()
	{
		updateMessage(null);
	}

	/**
	 * This method is invoked when the page is going to be left because of any 
	 * reason. Such condation can be: pressing Finish, or Cancel button on the 
	 * WizardDialog, or going to another wizard page back or forward.
	 * 
	 * @param action See ACTION_* constats in WizardPage
	 * @return If it returns false, then the page changing is cancelled.
	 */
	public abstract boolean leave( int action );

	/**
	 * This method is invoked for any changes on the registered components. Overriding this method
	 * you can continuosly verify the content of the wizard page, whether it containing all of the 
	 * necessary information in the correct form. If something is wrong, you can display message 
	 * to the user using the <code>context.getDialog().showWarning( "..." );</code> expression.
	 * 
	 * @param component This is the source component of the change. The updateMessage() method may 
	 * be invoked directly wihtout component event. In this case this argument is null.
	 */
	protected void updateMessage( final Component component )
	{
		// there is no default behaviour
	}

	protected void addChangeListener( final Component component )
	{
		if ( component instanceof JTextComponent )
		{
			final JTextComponent text = (JTextComponent) component;
			text.getDocument().addDocumentListener(new DocumentListener()
			{

				@Override
				public void removeUpdate( final DocumentEvent e )
				{
					updateMessage(text);

				}

				@Override
				public void insertUpdate( final DocumentEvent e )
				{
					updateMessage(text);

				}

				@Override
				public void changedUpdate( final DocumentEvent e )
				{
					updateMessage(text);

				}
			});
		}
		else if ( component instanceof JComboBox )
		{
			final JComboBox combo = (JComboBox) component;
			combo.addItemListener(new ItemListener()
			{

				@Override
				public void itemStateChanged( final ItemEvent arg0 )
				{
					updateMessage(combo);
				}
			});
		}
		else if ( component instanceof AbstractButton )
		{
			final AbstractButton button = (AbstractButton) component;
			button.addChangeListener(new ChangeListener()
			{

				@Override
				public void stateChanged( final ChangeEvent e )
				{
					updateMessage(button);
				}
			});
		}
		else
			throw new IllegalArgumentException("Unknown component type: " + component.getClass().getName());

	}
}
